package sfs

import (
	"context"
	"github.com/chicken-team-outside/chicken_transmission/api_messages"
	"github.com/chicken-team-outside/chicken_transmission/driver"
	"github.com/chicken-team-outside/chicken_transmission/utils"
	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"
	"io"
	"os"
)

type OpenFileQuery struct {
	Name string      `json:"name"`
	Flag int         `json:"flag"`
	Perm os.FileMode `json:"perm"`
}

/*
WriteFile 向指定设备写入数据,写入成功则返回 nil

	ctx: 取消当前操作
	clientId: 写入目的设备ID
	path: 写入目标设备的路径
	offset: 写入到目的路径的偏移量
	data: 需要写入的数据大小
	dataOffset: 需要写入数据的偏移量
	size: data参数的数据长度
*/
func (p *FsClient) WriteFile(ctx context.Context, clientId uint64, path string, offset int64, data io.ReadSeeker, dataOffset, size int64) error {
	timeout := p.timeout.Load()
	query := &api_messages.WriteFileQuery{
		Timeout:    int32(timeout),
		Size:       size,
		Offset:     offset,
		Path:       path,
		DataOffset: dataOffset,
	}
	var err error
	if size <= p.directSendMaxSize.Load() {
		query.Data = make([]byte, size)
		_, err = io.ReadFull(data, query.Data)
		if err == nil {
			query.Sha1 = utils.Sha1(query.Data, query.Sha1)
		}
	} else {
		query.Link, query.Sha1, err = p.uploadBlock(ctx, data, int64(size), nil)
		if err != nil {
			return err
		}
	}
	handler := NewOperatorKeeperClient(ctx, p, timeout)
	defer handler.Close()
	_, err = handler.QueryAndWait(clientId, api_messages.MessageType_WriteFile, query)
	return err
}

func (p *FsClient) writeFileProvider(ctx context.Context, header *api_messages.MessageHeader, data []byte) {
	query := new(api_messages.WriteFileQuery)
	err := proto.Unmarshal(data, query)
	if err != nil {
		zap.L().Warn("failed to unmarshal query", zap.Error(err), zap.Any("header", header))
		return
	}
	keeper := NewOperatorKeeperServer(context.Background(), header.ClientId, header.Id, p, query.Timeout)
	defer keeper.Close()
	go keeper.keepAlive()
	var offset int64
	var reader io.ReadCloser
	if err == nil {
		if query.Link == nil {
			reader = utils.NewBytesReadCloser(query.Data, int(query.DataOffset))
		} else {
			link := query.Link
			if err == nil {
				reader, err = p.downloadBlock(keeper.ctx, query.DataOffset, query.Size, link)
			}
		}
	}
	var localDriver driver.LocalDriver
	if err == nil {
		defer func() {
			if err01 := reader.Close(); err01 != nil {
				zap.L().Warn("failed to close reader", zap.Error(err01))
			}
		}()
		localDriver = p.GetLocal()
		if localDriver == nil {
			err = ErrNotSetLocalDriver
		}
	}
	zap.L().Debug("write file to driver", zap.String("path", query.Path), zap.Int64("offset", offset), zap.Any("header", header), zap.Any("query", query), zap.Error(err))
	var writer driver.LocalDriverFile
	if err == nil {
		writer, err = localDriver.OpenFile(query.Path, os.O_WRONLY|os.O_CREATE, 0666)
	}
	if err == nil {
		defer func() {
			if err01 := writer.Close(); err01 != nil {
				zap.L().Warn("failed to close driver file", zap.String("path", query.Path), zap.Error(err01))
			}
		}()
		_, err = writer.Seek(offset, io.SeekStart)
	}
	if err == nil {
		_, err = io.Copy(writer, reader)
	}
	if err != nil {
		if err01 := keeper.sendError(err); err01 != nil {
			zap.L().Warn("failed to send error message to client", zap.Error(err01), zap.Any("header", header), zap.Any("query", query), zap.Any("original_error", err))
		}
		return
	}
	err = keeper.success(nil)
	if err != nil {
		zap.L().Warn("failed to send success message to client", zap.Error(err), zap.Any("header", header), zap.Any("query", query))
	}
}
